package org.nlp2rdf.navigator.benchmark;

import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.log4j.Logger;
import org.dllearner.utilities.Helper;
import org.dllearner.utilities.datastructures.SetManipulation;
import org.dllearner.utilities.experiments.ExMakerFixedSize;
import org.dllearner.utilities.experiments.Examples;
import org.nlp2rdf.navigator.api.KnowledgeBase;
import org.nlp2rdf.navigator.client.common.ClassificationResultDTO;
import org.nlp2rdf.navigator.client.common.ExampleDTO;
import org.nlp2rdf.navigator.client.common.LearnedConceptDTO;
import org.nlp2rdf.navigator.component.learn.Learn;

public class LearnOne {
	private static final Logger logger = Logger.getLogger(LearnOne.class);

	public StringBuffer log = new StringBuffer();

	// private static org.slf4j.Logger logger =
	// LoggerFactory.getLogger(LearnOne.class);

	private String ignoredConcept = null;
	private final KnowledgeBase mySession;

	public int exampleSize = 10;
	public int iterations = 4;
	public Execute config;

	public LearnOne(KnowledgeBase s, Execute config) {
		mySession = s;
		this.config = config;
	}

	public void doOneOntologyClass(String classUri, final Set<String> positives, final Set<String> negatives)
			throws Exception {
		ignoredConcept = classUri;
		doIt(positives, negatives);
	}

	public void doOneCategory(final Set<String> positives, final Set<String> negatives) throws Exception {
		ignoredConcept = null;
		doIt(positives, negatives);
	}

	private void doIt(final Set<String> positives, final Set<String> negatives) throws Exception {
		Examples current = new Examples();
		current.addPosTest(positives);
		current.addNegTest(negatives);
		current = new ExMakerFixedSize(current).select(exampleSize);
		logger.info(current.toString());

		for (int i = 0; i < config.sizeOfResultVector; i++) {
//			logger.info(current.toFullString());
			
			String ret = "Training:\n";
			for (String one : current.getPosTrain()) {
				ret += "+" + one + "\n";
			}
			for (String one : current.getNegTrain()) {
				ret += "-" + one + "\n";
			}
			logger.info(ret);
			
			SortedSet<String> retrieved = new TreeSet<String>();
			retrieved = learnNClassify(current);

			measure(retrieved, current, i);
			SortedSet<String> newlyFoundPos = Helper.intersection(current.getPosTest(), retrieved);
			SortedSet<String> retrievedNoPositives = Helper.difference(retrieved, current
					.getPositiveExamples());
			// SortedSet<String> newlyFoundNeg =
			// Helper.difference(retrievedNoNeg ,
			// current.getNegativeExamples());
			log.append("retrieved: " + retrieved.size() + "\n");
			log.append("posfound " + newlyFoundPos.size() + "\n");
			logger.info(newlyFoundPos.size() + "");
			logger.info("" + retrievedNoPositives.size());

			current.addPosTrain(SetManipulation.fuzzyShrink(newlyFoundPos, exampleSize));
			current.addNegTrain(SetManipulation.fuzzyShrink(retrievedNoPositives, exampleSize));
			logger.info("TOTAL: \n" + log.toString());
			
			// tmp.add
			// if (i==1) {
			// System.exit(0);
			// }

		}

	}

	private String measure(SortedSet<String> retrieved, Examples ex, int iteration) {

		// double posAsPos = ex.recall(retrieved) * (double)
		// ex.sizeTotalOfPositives();
		// String ret = "Recall: " + (posAsPos / (double)
		// ex.getPosTest().size()) + "\n";
		// SortedSet<String> tmp = new TreeSet<String>(retrieved);
		// tmp.removeAll(ex.getTrainExamples());

		double recall = ex.recall(retrieved);

		logger.info("retrieved " + retrieved.size());
		logger.info("posAsPos " + Helper.intersection(retrieved, ex.getPosTest()).size());
		logger.info("posTest " + ex.getPosTest().size());

		config.add(Execute.logRecall, iteration, recall);
		double precision = ex.precision(retrieved);
		config.add(Execute.logPrecision, iteration, precision);
		config.add(Execute.logFMeasure, iteration, fmeasure(precision, recall));

		String ret = "Recall: " + recall + "\n";
		ret += "precision: " + precision;
		logger.info(ret);
		log.append("Measure result: " + ret + "\n");
		return ret;

	}

	public static double fmeasure(double precision, double recall) {
		double fmeasure = (precision + recall == 0) ? 0.0d : (2 * precision * recall) / (precision + recall);
		logger.debug("F-Measure: " + new DecimalFormat("00.###%").format(fmeasure));
		return fmeasure;
	}

	private SortedSet<String> learnNClassify(Examples x) throws Exception {
		Learn la = (Learn) mySession.getLearn();
		if (ignoredConcept != null) {
			logger.info("ignoring: " + ignoredConcept);
			la.setIgnoredConcepts(new HashSet<String>(Arrays.asList(new String[] { ignoredConcept })));
		}
		la.setNoisePercentage(config.noisePercentage);
		if(config.ignoreProperty != null) {
			//TODO
//			KsSPARQLComponent ks = ((KsSPARQLComponent)mySession.getKnowledgeSources());
//			ks.getPredList().add(config.ignoreProperty);
		}
		
		LearnedConceptDTO res = la.learn(x);
		log.append(res.getKbSyntaxString() + "\n");
		logger.info("Learned: " + res.getKbSyntaxString());
		ClassificationResultDTO clasret = mySession.classify(res.getKbSyntaxString(), 0, 0);
		SortedSet<String> retrieved = new TreeSet<String>();
		for (ExampleDTO string : clasret.getRetrievedExamples()) {
			retrieved.add(string.getUri());
		}
		return retrieved;
	}

}
